package 算法;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * @author zhangmin
 * @create 2021-12-16 9:34
 */
public class Simulate06 {
    /**==============================================================================================================================================
     * JZ29 顺时针打印矩阵
     * 输入一个矩阵，按照从外向里以顺时针的顺序依次打印出每一个数字
     * 按照右下左上的顺序改变移动方向，direIndex=(direIndex+1)%4;标记移动方向,visited标记是否访问过
     *  */
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        if(matrix.length==0||matrix[0].length==0) return new ArrayList<>();
        int m=matrix.length,n=matrix[0].length;
        int total=m*n;
        boolean[][] visited=new boolean[m][n];
        int[][] dir={{0,1},{1,0},{0,-1},{-1,0}};
        int direIndex=0,row=0,col=0;
        ArrayList<Integer> res=new ArrayList<>();
        for (int i = 0; i < total; i++) {
            res.add(matrix[row][col]);
            visited[row][col]=true;
            int nextrow=row+dir[direIndex][0],nextcol=col+dir[direIndex][1];
            if (nextrow<0||nextrow>=m||nextcol<0||nextcol>=n||visited[nextrow][nextcol]){
                //到达边界,换方向
                direIndex=(direIndex+1)%4;
            }
            row=row+dir[direIndex][0];
            col=col+dir[direIndex][1];
        }
        return res;
    }

    /**==============================================================================================================================================
     * JZ61 扑克牌顺子
     * 现在有2副扑克牌，从扑克牌中随机五张扑克牌，我们需要来判断一下是不是顺子。有如下规则：
     * 1. A为1，J为11，Q为12，K为13，A不能视为14
     * 2. 大、小王为 0，0可以看作任意牌
     * 3. 如果给出的五张牌能组成顺子（即这五张牌是连续的）就输出true，否则就输出false。
     * 4.数据保证每组5个数字，每组最多含有4个零，数组的数取值为 [0, 13]
     * 设此 5张牌中最大的牌为 max ，最小的牌为 min （大小王除外），则需满足：max - min < 5
     *  */
    public boolean IsContinuous(int [] numbers) {
        Arrays.sort(numbers);
        int zerocount=0;
        int count=0;
        for (int i = 0; i < numbers.length; i++) {
            if (numbers[i]==0) zerocount++;
            else {
                if (i>0&&numbers[i]==numbers[i-1])return false;
                if (i>0&&numbers[i-1]!=0){
                    count+=(numbers[i]-numbers[i-1])-1;
                }
            }
        }
        return zerocount>=count;
    }

    /**==============================================================================================================================================
     * JZ67 把字符串转换成整数(atoi)
     * 写一个函数 StrToInt，实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。传入的字符串可能有以下部分组成:
     * 1.若干空格 2.（可选）一个符号字符（'+' 或 '-'） 3. 数字，字母，符号，空格组成的字符串表达式 4. 若干空格
     *
     * 转换算法如下:
     * 1.去掉无用的前导空格
     * 2.第一个非空字符为+或者-号时，作为该整数的正负号，如果没有符号，默认为正数
     * 3.判断整数的有效部分：
     * 3.1 确定符号位之后，与之后面尽可能多的连续数字组合起来成为有效整数数字，如果没有有效的整数部分，那么直接返回0
     * 3.2 将字符串前面的整数部分取出，后面可能会存在存在多余的字符(字母，符号，空格等)，这些字符可以被忽略，它们对于函数不应该造成影响
     * 3.3 整数超过 32 位有符号整数范围 [−2^31,  2^31 − 1] ，需要截断这个整数，使其保持在这个范围内。具体来说，小于 −2^31的整数应该被调整为 −2^31 ，大于 2^31 − 1 的整数应该被调整为 2^31 − 1
     * 4.去掉无用的后导空格
     *  */
    public int StrToInt (String s) {
        int n=s.length();
        if (n==0) return 0;
        int sign=1,index=0,res=0;
        int IntMax=Integer.MAX_VALUE/10;//取出最大值去掉最后一位，用于判断是否越界
        //跳过前置空格
        while (s.charAt(index)==' ') {
            if (++index==n) return 0;
        }
        if (s.charAt(index)=='-') sign=-1;
        if (s.charAt(index)=='-'||s.charAt(index)=='+') index++;
        for (int i = index; i < n; i++) {
            if (s.charAt(i)<'0'||s.charAt(i)>'9')break;;
            if (res>IntMax||(res==IntMax&&s.charAt(i)>'7')){
                return sign>0?Integer.MAX_VALUE:Integer.MIN_VALUE;
            }
            res=res*10+s.charAt(i)-'0';
        }
        return sign*res;
    }

    /**==============================================================================================================================================
     * JZ20 表示数值的字符串
     * 请实现一个函数用来判断字符串str是否表示数值（包括科学计数法的数字，小数和整数）。
     * 科学计数法的数字(按顺序）可以分成以下几个部分:
     * 1.若干空格
     * 2.一个整数或者小数
     * 3.（可选）一个 'e' 或 'E' ，后面跟着一个整数(可正可负)
     * 4.若干空格
     *
     * 小数（按顺序）可以分成以下几个部分：
     * 1.若干空格
     * 2.（可选）一个符号字符（'+' 或 '-'）
     * 3. 可能是以下描述格式之一:
     * 3.1 至少一位数字，后面跟着一个点 '.'
     * 3.2 至少一位数字，后面跟着一个点 '.' ，后面再跟着至少一位数字
     * 3.3 一个点 '.' ，后面跟着至少一位数字
     * 4.若干空格
     *
     * 整数（按顺序）可以分成以下几个部分：
     * 1.若干空格
     * 2.（可选）一个符号字符（'+' 或 '-')
     * 3. 至少一位数字
     * 4.若干空格
     *  */
    int index=0;
    //判断是否是有符号整数
    boolean isInt(String str){
        if (index==str.length()) return false;
        if (str.charAt(index)=='-'||str.charAt(index)=='+'){
            index++;
        }
        return isUnsignInt(str);
    }
    boolean isUnsignInt(String str){
        int start=index;
        while (index!=str.length()&&str.charAt(index)>='0'&&str.charAt(index)<='9') index++;
        return index>start;
    }
    // 数字的格式可以用A[.[B]][e|EC]或者.B[e|EC]表示，
    // 其中A和C都是整数（可以有正负号，也可以没有），而B是一个无符号整数
    public boolean isNumeric (String str) {
        int n=str.length();
        if (n==0) return false;

        //跳过空格
        while (index!=n&&str.charAt(index)==' ') index++;
        if (index==n) return false;
        //数值部分
        boolean res=isInt(str);
        if (index==n) return res;
        //判断是否是小数
        if (str.charAt(index)=='.'){
            index++;
            // 下面一行代码用||的原因：
            // 1. 小数可以没有整数部分，例如.123等于0.123；
            // 2. 小数点后面可以没有数字，例如233.等于233.0；
            // 3. 当然小数点前面和后面可以有数字，例如233.666
            res=isUnsignInt(str)||res;
        }
        if (index==n) return res;
        //判断是否是科学计数法
        if (str.charAt(index)=='e'||str.charAt(index)=='E'){
            index++;
            // 下面一行代码用&&的原因：
            // 1. 当e或E前面没有数字时，整个字符串不能表示数字，例如.e1、e1；
            // 2. 当e或E后面没有整数时，整个字符串不能表示数字，例如12e、12e+5.4
            res=res&&isInt(str);
        }
        while (index!=n&&str.charAt(index)==' ') index++;
        return res&&index==n;
    }
}
